import redis
import time
import json

def set_params(params):
    db = redis.Redis(host="localhost", port=6379, decode_responses=True)
    for param in params:
        db.set(param, params[param])
    return True


def init_stations():
    db = redis.Redis(host="localhost", port=6379, decode_responses=True)
    db.delete("station_list")
    db.delete("F_QUEUE")
    db.delete("T_QUEUE")
    F_num = int(db.get("F_NUM"))
    T_num = int(db.get("T_NUM"))
    time_stamp = get_system_time()
    for i in range(F_num):
        station_id = "F" + str(i+1)
        db.hset(station_id, "type", "F")
        db.delete(station_id + "_QUEUE")
        db.zadd("F_STATION", {station_id: time_stamp})
        db.sadd("station_list", station_id)
        db.hset(station_id, "is_on", "1")
        db.hset(station_id, "is_serve", "0")
        db.hset(station_id, "serves", "0")
        db.hset(station_id, "time_finish", "0")
        db.hset(station_id, "power_finish", "0")
        db.hset(station_id, "last_serve", "0")
        db.hset(station_id, "serve_id", "0")

        report_id = "REPORT_" + station_id
        db.hset(report_id, "all_fees", "0")
        db.hset(report_id, "all_power", "0")
        db.hset(report_id, "all_power_fees", "0")
        db.hset(report_id, "all_serve_fees", "0")
        db.hset(report_id, "all_serves", "0")
        db.hset(report_id, "all_time", "0")
        db.hset(report_id, "date", "0")
        db.hset(report_id, "id", "0")

    for i in range(T_num):
        station_id = "T" + str(i+1)
        db.hset(station_id, "type", "T")
        db.delete(station_id + "_QUEUE")
        db.zadd("T_STATION", {station_id: time_stamp})
        db.sadd("station_list", station_id)
        db.hset(station_id, "is_on", "1")
        db.hset(station_id, "is_serve", "0")
        db.hset(station_id, "serves", "0")
        db.hset(station_id, "time_finish", "0")
        db.hset(station_id, "power_finish", "0")
        db.hset(station_id, "last_serve", "0")
        db.hset(station_id, "serve_id", "0")

        report_id = "REPORT_" + station_id
        db.hset(report_id, "all_fees", "0")
        db.hset(report_id, "all_power", "0")
        db.hset(report_id, "all_power_fees", "0")
        db.hset(report_id, "all_serve_fees", "0")
        db.hset(report_id, "all_serves", "0")
        db.hset(report_id, "all_time", "0")
        db.hset(report_id, "date", "0")
        db.hset(report_id, "id", "0")


def set_system_time(time_string="2022-6-1 6:00:00"):
    db = redis.Redis(host="localhost", port=6379, decode_responses=True)
    time_tuple = time.strptime(time_string, '%Y-%m-%d %H:%M:%S')
    system_time = time.mktime(time_tuple)
    offset_time = time.time()
    db.set("SYSTEM_TIME", system_time)
    db.set("OFFSET_TIME", offset_time)
    return system_time


def get_system_time():
    db = redis.Redis(host="localhost", port=6379, decode_responses=True)
    current_time = time.time()
    offset_time = float(db.get("OFFSET_TIME"))
    system_time = float(db.get("SYSTEM_TIME"))
    time_rate = float(db.get("TIME_RATE"))
    system_time = system_time + (current_time - offset_time) * time_rate
    return system_time


def get_wait_queue(mode):
    db = redis.Redis(host="localhost", port=6379, decode_responses=True)
    wait_queue = db.zrange(mode + "_QUEUE", 0, -1)
    return wait_queue


def full_wait_queue():
    db = redis.Redis(host="localhost", port=6379, decode_responses=True)
    F_len = int(db.zcard("F" + "_QUEUE"))
    T_len = int(db.zcard("T" + "_QUEUE"))
    wait_len = int(db.get("WAIT_LEN"))
    if F_len + T_len < wait_len:
        return False
    else:
        return True


def total_wait_queue():
    db = redis.Redis(host="localhost", port=6379, decode_responses=True)
    db.zunionstore("WAIT_QUEUE", ["F_QUEUE", "T_QUEUE"])
    wait_queue = db.zrange("WAIT_QUEUE", 0, -1)
    return wait_queue


def push_wait_queue(mode, user_id):
    db = redis.Redis(host="localhost", port=6379, decode_responses=True)
    db.hset(user_id, "where", "wait")
    db.hset(user_id, "state", "N")
    time_stamp = get_system_time()
    db.zadd(mode + "_QUEUE", {user_id: time_stamp})
    return time_stamp


def pop_wait_queue(mode, user_id):
    db = redis.Redis(host="localhost", port=6379, decode_responses=True)
    time_stamp = get_system_time()
    db.zrem(mode + "_QUEUE", user_id)
    return time_stamp


def print_wait_queue():
    db = redis.Redis(host="localhost", port=6379, decode_responses=True)
    wait_queue = total_wait_queue()
    res = []
    for user_id in wait_queue:
        mode = db.hget(user_id, "mode")
        amount = db.hget(user_id, "amount")
        res.append((user_id, mode, amount))
    print(res)
    return res


def get_power_rate(mode):
    db = redis.Redis(host="localhost", port=6379, decode_responses=True)
    if mode == 'F':  # fast mode
        return int(db.get('FAST_POWER_RATE'))
    elif mode == 'T':  # slow mode
        return int(db.get("SLOW_POWER_RATE"))


def get_charge_queue(station_id):
    db = redis.Redis(host="localhost", port=6379, decode_responses=True)
    charge_queue = db.zrange(station_id + "_QUEUE", 0, -1)
    return charge_queue


def push_charge_queue(station_id, user_id, time_stamp):
    db = redis.Redis(host="localhost", port=6379, decode_responses=True)
    db.hset(user_id, "where", "charge")
    charge_len = int(db.get("CHARGE_LEN"))
    db.zadd(station_id + "_QUEUE", {user_id: time_stamp})
    mode = db.hget(user_id, "mode")
    prev_time_stamp = float(db.zscore(mode + "_STATION", station_id))
    db.zrem(mode + "_STATION", station_id)
    current_len = int(db.zcard(station_id + "_QUEUE"))
    if current_len == 0:
        prev_time_stamp = time_stamp
    if current_len < charge_len:
        order_id = db.hget(user_id, "order")
        charge_time = float(db.hget(order_id, "charge_time"))
        prev_time_stamp += charge_time
        db.zadd(mode + "_STATION", {station_id: prev_time_stamp})
    return prev_time_stamp


def print_charge_queue():
    db = redis.Redis(host="localhost", port=6379, decode_responses=True)
    time_rate = float(db.get("TIME_RATE"))
    stations = db.smembers("station_list")
    res = dict()
    for station in stations:
        s_res = []
        users = db.zrange(station + "_QUEUE", 0, -1)
        for user_id in users:
            if db.exists(station+":"+user_id):
                ttl = db.ttl(station+":"+user_id)
                order_id = db.hget(user_id, "order")
                charge_time = float(db.hget(order_id, "charge_time"))
                charge_time = charge_time - ttl * time_rate
                mode = db.hget(user_id, "mode")
                charge_power = charge_time / 3600 * get_power_rate(mode)
                start_time_stamp = float(db.hget(order_id, "start_time"))
                start_struct_time = time.localtime(start_time_stamp)
                lh, lm = start_struct_time.tm_hour, start_struct_time.tm_min
                end_struct_time = time.localtime(get_system_time())
                rh, rm = end_struct_time.tm_hour, end_struct_time.tm_min
                _, _, cost = get_order_cost(mode, lh, lm, rh, rm)
                s_res.append((user_id, charge_power, cost))
            else:
                flag = db.get("flag")
                if user_id == users[0] and user_id == flag:
                    db.zrem(station + "_QUEUE", user_id)
                else:
                    if user_id == users[0]:
                        db.set("flag", user_id)
                    s_res.append((user_id, 0, 0))

        res[station] = s_res
    print(res)
    return res


def get_available_station(mode):
    db = redis.Redis(host="localhost", port=6379, decode_responses=True)
    stations = db.zrange(mode + "_STATION", 0, -1)
    if stations:
        return stations[0]
    else:
        return []


def set_order(user_id, mode, amount, time_stamp):
    db = redis.Redis(host="localhost", port=6379, decode_responses=True)
    db.hset(user_id, "mode", mode)
    db.hset(user_id, "amount", amount)

    order_id = user_id + ":" + time.strftime("%Y%m%d%H%M", time.localtime(time_stamp))
    db.hset(user_id, "order", order_id)
    db.hset(order_id, "generate_time", time_stamp)
    db.hset(order_id, "amount", amount)

    power_rate = get_power_rate(mode)
    charge_time = float(amount) / power_rate * 3600
    db.hset(order_id, "charge_time", charge_time)
    db.hset(order_id, "is_payed", "no")
    return order_id


def start_order(user_id):
    db = redis.Redis(host="localhost", port=6379, decode_responses=True)
    time_stamp = get_system_time()
    order_id = db.hget(user_id, "order")
    db.hset(order_id, "start_time", str(time_stamp))

    station_id = db.hget(order_id, "station_id")
    charge_time = db.hget(order_id, "charge_time")

    time_rate = float(db.get("TIME_RATE"))
    db.set(station_id+":"+user_id, charge_time, ex=int(float(charge_time)/time_rate))

    mode = db.hget(user_id, "mode")
    db.hset(user_id, "state", mode)
    db.hset(station_id, "is_serve", "1")
    db.hset(station_id, "serve_id", user_id)
    return time_stamp


def end_order(user_id):
    db = redis.Redis(host="localhost", port=6379, decode_responses=True)
    db.hset(user_id, "where", "exit")
    db.hset(user_id, "state", "N")
    order_id = db.hget(user_id, "order")
    station_id = db.hget(order_id, "station_id")
    db.zrem(station_id + "_QUEUE", user_id)
    time_stamp = get_system_time()
    mode = db.hget(user_id, "mode")
    db.hset(order_id, "end_time", str(time_stamp))
    start_time = db.hget(order_id, "start_time")
    start_time_stamp = float(start_time)
    charge_time = time_stamp - start_time_stamp
    db.hset(order_id, "charge_time", str(charge_time))

    start_struct_time = time.localtime(start_time_stamp)
    lh, lm = start_struct_time.tm_hour, start_struct_time.tm_min
    end_struct_time = time.localtime(start_time_stamp)
    rh, rm = end_struct_time.tm_hour, end_struct_time.tm_min
    charge_cost, service_cost, cost = get_order_cost(mode, lh, lm, rh, rm)
    db.hset(order_id, "charge_cost", charge_cost)
    db.hset(order_id, "service_cost", service_cost)
    db.hset(order_id, "cost", cost)

    report_id = "REPORT_" + station_id
    all_fees = float(db.hget(report_id, "all_fees"))
    all_fees += cost
    db.hset(report_id, "all_fees", str(all_fees))
    all_power = float(db.hget(report_id, "all_power"))
    amount = float(db.hget(order_id, "amount"))
    all_power += amount
    db.hset(report_id, "all_power", str(all_power))

    db.hset(station_id, "is_serve", "0")
    time_finish = float(db.hget(station_id, "time_finish"))
    time_finish += charge_time
    db.hset(station_id, "time_finish", str(time_finish))
    power_finish = float(db.hget(station_id, "power_finish"))
    power_finish += amount
    db.hset(station_id, "power_finish", str(power_finish))
    db.hset(station_id, "last_serve", user_id)

    serves = db.hget(station_id, "serves")
    db.hset(station_id, "serves", str(int(serves) + 1))

    stations = get_available_station(mode)
    if station_id not in stations:
        db.zadd(mode + "_STATION", {station_id: time_stamp})

    res = {
        "order_id": ""
    }

    order_id = db.hget(user_id, "order")
    res["order_id"] = order_id
    res["charge_time"] = float(db.hget(order_id, "charge_time"))

    generate_time_stamp = float(db.hget(order_id, "generate_time"))
    start_time_stamp = float(db.hget(order_id, "start_time"))
    end_time_stamp = float(db.hget(order_id, "end_time"))
    res["generate_time"] = time.strftime("%Y-%m-%d %H:%M", time.localtime(generate_time_stamp))
    res["start_time"] = time.strftime("%Y-%m-%d %H:%M", time.localtime(start_time_stamp))
    res["end_time"] = time.strftime("%Y-%m-%d %H:%M", time.localtime(end_time_stamp))

    res["charge_cost"] = db.hget(order_id, "charge_cost")
    res["service_cost"] = db.hget(order_id, "service_cost")
    res["cost"] = db.hget(order_id, "cost")

    car_order_path = './data/' + user_id + '.json'
    with open(car_order_path, 'w') as f:
        order_json = db.hgetall(order_id)
        f.write(json.dumps(order_json))

    report_path = './data/' + report_id + '.json'
    with open(report_path, 'w') as f:
        report_json = db.hgetall(report_id)
        report_json['id'] = station_id
        report_json['date'] = str(time.localtime(time_stamp).tm_mon) + '/' + str(time.localtime(time_stamp).tm_mday)
        f.write(json.dumps(report_json))

    return time_stamp


def get_order_cost(mode, lh, lm, rh, rm):
    db = redis.Redis(host="localhost", port=6379, decode_responses=True)
    peek = float(db.get("PEEK"))
    plain = float(db.get("PLAIN"))
    valley = float(db.get("VALLEY"))
    service = float(db.get("SERVICE"))

    power_rate = 0
    if mode == "F":
        power_rate = int(db.get("FAST_POWER_RATE"))
    if mode == "T":
        power_rate = int(db.get("SLOW_POWER_RATE"))

    schedule = [[0, 7], [7, 10], [10, 15], [15, 18], [18, 21], [21, 23], [23, 24]]
    cost = [valley, plain, peek, plain, peek, plain, valley]

    l, r, charge_cost = 0, 0, 0
    for i in range(len(schedule)):
        if schedule[i][0] <= lh < schedule[i][1]:
            l = i
        if schedule[i][0] <= rh < schedule[i][1]:
            r = i
    if lh <= rh:
        if l == r:
            charge_cost = ((rh - lh) * 60 + (rm - lm)) / 60 * power_rate * cost[l]
        else:
            charge_cost += ((schedule[l][1] - lh) * 60 - lm) / 60 * power_rate * cost[l]
            charge_cost += ((rh - schedule[r][0]) * 60 + rm) / 60 * power_rate * cost[r]
            for j in range(l + 1, r):
                charge_cost += (schedule[j][1] - schedule[j][0]) * power_rate * cost[j]
        service_cost = ((rh - lh) * 60 + (rm - lm)) / 60 * power_rate * service
        cost = charge_cost + service_cost
    else:
        charge_cost += ((schedule[l][1] - lh) * 60 - lm) / 60 * power_rate * cost[l]
        charge_cost += ((rh - schedule[r][0]) * 60 + rm) / 60 * power_rate * cost[r]
        for j in range(l+1, 7):
            charge_cost += (schedule[j][1] - schedule[j][0]) * power_rate * cost[j]
        for j in range(0, r):
            charge_cost += (schedule[j][1] - schedule[j][0]) * power_rate * cost[j]
        service_cost = ((rh - lh) * 60 + (rm - lm) + 24 * 60) / 60 * power_rate * service
        cost = charge_cost + service_cost
    return charge_cost, service_cost, cost
